﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Forms;
using System.IO;
using System.Xml.Serialization;
using System.Net;
using System.ComponentModel;
using System.Threading;
using Wintellect.PowerCollections;
using System.Text.RegularExpressions;
using TileDownLoader.Utils;
using TileDownLoader.Kml;
using TileDownLoader.Elevation;
using System.Threading.Tasks;

namespace TileDownLoader
{
    public partial class MainWindow : Window
    {
        private BackgroundWorker m_downloadWorker = null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <see cref="www.google.com"/ >
        private void bttnBrowse_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog fileDialog = new OpenFileDialog();
            fileDialog.DefaultExt = ".mapprj";
            fileDialog.Filter = "Map Project|*.mapprj";
            fileDialog.InitialDirectory = Environment.CurrentDirectory;

            if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtMapPrjpath.Text = fileDialog.FileName;
            }
        }

        private void bttnBrowseFolder_Click(object sender, RoutedEventArgs e)
        {
            FolderBrowserDialog browseDialog = new FolderBrowserDialog();
            //browseDialog.RootFolder = Environment.SpecialFolder.MyComputer;
            if (browseDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                txtOutputFolder.Text = browseDialog.SelectedPath;
                txtInputFolder.Text = browseDialog.SelectedPath;
            }
        }

        private void bttnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.DownloadWorker.CancelAsync();

        }

        private string UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.7) Gecko/20091221 Firefox/3.5.7";
        /// <summary>
        /// timeout for map connections
        /// </summary>
        private int Timeout = 30 * 1000;

        private void bttnDownloadTiles_Click(object sender, RoutedEventArgs e)
        {
            if ((!string.IsNullOrWhiteSpace(txtMapPrjpath.Text)) && File.Exists(txtMapPrjpath.Text) &&
               (!string.IsNullOrWhiteSpace(txtOutputFolder.Text)) && Directory.Exists(txtOutputFolder.Text))
            {
                Url[] allImages;

                using (FileStream fs = File.Open(txtMapPrjpath.Text, FileMode.Open, FileAccess.Read))
                {
                    XmlSerializer ser = new XmlSerializer(typeof(Url[]));

                    allImages = ser.Deserialize(fs) as Url[];
                }

                m_downloadWorker = new BackgroundWorker();

                #region Main Worker
                m_downloadWorker.DoWork += (senderLocal, eLocal) =>
                {

                    RunWorkArg arg = eLocal.Argument as RunWorkArg;

                    try
                    {

                         ParallelOptions options = new ParallelOptions();

                        options.MaxDegreeOfParallelism = 4;

                        int index = 0;

                        Parallel.ForEach<Url>(arg.AllImages, options, item => {

                            if (this.DownloadWorker.CancellationPending)
                            {
                                arg.Action = Action.Canceled;
                                return;
                            }

                            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(item.Src);

                            request.UserAgent = UserAgent;
                            request.Timeout = Timeout;
                            request.ReadWriteTimeout = Timeout * 6;
                            request.Accept = "*/*";

                            using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                            {
                                using (Stream responseStream = response.GetResponseStream())
                                {
                                    using (FileStream writer = File.OpenWrite(String.Format("{0}\\{1}", arg.OutputFolder, item.Target)))
                                    {
                                        responseStream.CopyTo(writer);
                                    }
                                }
                            }

                            Interlocked.Increment(ref index);

                            m_downloadWorker.ReportProgress((int)(((double)index / (double)arg.AllImages.Length) * 100.0), string.Format("Downloaded {0} of {1} Tiles.", index, arg.AllImages.Length));
                        
                        });

                        #region Single Threaded
                        //int index = 0;

                        //foreach (Url item in arg.AllImages)
                        //{
                        //    Thread.Sleep(100);

                        //    if (this.DownloadWorker.CancellationPending)
                        //    {
                        //        arg.Action = Action.Canceled;
                        //        break;
                        //    }

                        //    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(item.Src);

                        //    request.UserAgent = UserAgent;
                        //    request.Timeout = Timeout;
                        //    request.ReadWriteTimeout = Timeout * 6;
                        //    request.Accept = "*/*";

                        //    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                        //    {
                        //        using (Stream responseStream = response.GetResponseStream())
                        //        {
                        //            using (FileStream writer = File.OpenWrite(String.Format("{0}\\{1}", arg.OutputFolder, item.Target)))
                        //            {
                        //                responseStream.CopyTo(writer);
                        //            }
                        //        }
                        //    }

                        //    index++;

                        //    m_downloadWorker.ReportProgress((int)(((double)index / (double)arg.AllImages.Length) * 100.0), string.Format("Downloaded {0} of {1} Tiles.", index, arg.AllImages.Length));
                        //}
                        #endregion

                        if (this.KmlWorker != null)
                        {
                            this.KmlWorker.ReportProgress(100);
                        }
                    }
                    catch (Exception ex)
                    {
                        arg.Action = Action.Error;
                        arg.Error = ex;
                    }

                    eLocal.Result = arg;

                };
                #endregion

                #region Worker Completed
                m_downloadWorker.RunWorkerCompleted += (senderLocal, eLocal) =>
                {

                    RunWorkArg arg = eLocal.Result as RunWorkArg;

                    if (arg.Action == Action.Success)
                    {
                        System.Windows.MessageBox.Show(String.Format("Total {0} Tiles are downloaded.", arg.AllImages.Count()));
                    }
                    else if (arg.Action == Action.Canceled)
                    {
                        System.Windows.MessageBox.Show("User Canceled the Tiles Download Operation.");
                    }
                    else
                    {
                        bttnDownloadTiles.Background = Brushes.Red;

                        if (eLocal.Error != null)
                        {
                            System.Windows.MessageBox.Show(String.Format("Tile Download Error:{0}.", eLocal.Error.Message));

                        }
                        else
                        {
                            if (eLocal.Result != null && eLocal.Result is RunWorkArg)
                            {
                                RunWorkArg err = eLocal.Result as RunWorkArg;

                                if (err.Error != null)
                                {
                                    System.Windows.MessageBox.Show(String.Format("Tile Download Error:{0}.", err.Error.Message));
                                }
                                else
                                {
                                    System.Windows.MessageBox.Show("Tile Download Error:Unknown error occured");
                                }
                            }
                            else
                            {
                                System.Windows.MessageBox.Show("Tile Download Error:Unknown error occured");
                            }
                        }
                    }
                };
                #endregion

                #region Main Worker Progress Update
                m_downloadWorker.ProgressChanged += (senderLocal, eLocal) =>
                {
                    lblDwnStatus.Content = eLocal.UserState;
                    downProgress.Value = eLocal.ProgressPercentage;
                };
                #endregion

                m_downloadWorker.WorkerReportsProgress = true;
                m_downloadWorker.WorkerSupportsCancellation = true;
                m_downloadWorker.RunWorkerAsync(new RunWorkArg(allImages, txtOutputFolder.Text));
            }
            else
            {
                System.Windows.MessageBox.Show("Please select proper map project file and output folder");
            }

        }

        #region Tile Download supporting Tiles
        enum Action
        {
            Success,
            Canceled,
            Error
        }

        class RunWorkArg
        {
            private Url[] allImages;

            public Url[] AllImages
            {
                get { return allImages; }
            }

            private string outputFolder;

            public string OutputFolder
            {
                get { return outputFolder; }
            }
            public RunWorkArg(Url[] allImages, string outputfolder)
            {
                this.allImages = allImages;
                this.outputFolder = outputfolder;
            }

            private Action action = Action.Success;

            public Action Action
            {
                get { return action; }
                set { action = value; }
            }

            private Exception error = null;

            public Exception Error
            {
                get { return error; }
                set { error = value; }
            }

        }
        #endregion
    }
}
